---
title: Extract pages
summary: Produces a new PDF containing only the selected page ranges.
status: draft
owner: SAIF Doc Gen
last_reviewed: 2026-09-17
library: PdfAPI.Core
reference_type: package
operation_id: OP-08
trigger: PdfClientService.ExtractPages(DocumentRange) / ExtractPageRanges(PdfReader, DocumentRange) invoked
---

# Extract pages

This is the most representative behaviour-bearing operation in the library: it copies a caller-selected set of page ranges from a source PDF into a brand-new output document via iTextSharp's `PdfCopy`.

| Field | Value |
| --- | --- |
| **Operation ID** | OP-08 |
| **Trigger** | Caller invokes `ExtractPages(DocumentRange documentRange)`, which opens a `PdfReader` and delegates to `ExtractPageRanges(PdfReader, DocumentRange)` |
| **Inputs** | `DocumentRange` (a `Document` paired with one or more `PageRange` selections) |
| **Outputs** | `PdfResult` in `Succeeded` status, wrapping a new `Document` containing the extracted pages |
| **Primary components** | `PdfAPI.Core.PdfClientService.ExtractPages`, `PdfAPI.Core.PdfClientService.ExtractPageRanges` |
| **Entry point** | `PdfAPI.Core\Services\PdfClientService.cs` |

## Steps

1. Read `documentRange.Document.File` and run [Validate PDF bytes](op-01-validate-pdf-bytes.md) to guard it. ([BR-01](../business-rules.md#br-01-pdf-signature-validation), [BR-02](../business-rules.md#br-02-null-bytes-are-never-a-pdf), [BR-03](../business-rules.md#br-03-invalid-pdf-input-aborts-with-pdfexception))
2. Open a `PdfReader` over the source bytes and hand it, with the range, to `ExtractPageRanges`.
3. Create an output `iTextSharp.text.Document` and a `PdfCopy` writer over a `MemoryStream`; open the document.
4. Read `sourceDocReader.NumberOfPages` as the total page count.
5. For each `PageRange`:
   - Skip the range when both start and stop exceed the total page count. ([BR-12](../business-rules.md#br-12-out-of-range-page-ranges-are-skipped))
   - Clamp start and stop down to the total page count when either exceeds it. ([BR-13](../business-rules.md#br-13-page-boundaries-are-clamped-to-page-count))
   - When the range `IsOriginal` (1 to `-1`), append the entire source document via `PdfCopy.AddDocument`. ([BR-14](../business-rules.md#br-14-original-range-copies-the-whole-document))
   - Else when `stop >= start` or `stop == -1`, treat `-1` as the last page and append pages ascending from start to stop. ([BR-15](../business-rules.md#br-15-open-ended-stop-page-means-through-last-page))
   - Else (`stop < start`) append pages descending from start down to stop, producing a reversed page order. ([BR-16](../business-rules.md#br-16-descending-range-reverses-page-order))
6. Materialise the output bytes, wrap them in a new `Document` with extension `"pdf"` via `Document.Load(byte[], string)`, and build a `PdfResult` in `Succeeded` status. ([BR-17](../business-rules.md#br-17-successful-extraction-returns-a-new-succeeded-result))
   - Delegates to [Load a document](op-09-load-a-document.md).
7. Set `PdfResult.PdfProcessorType = iTextSharp` and `PdfResult.Message = documentRange.ToString()` (the rendered page expression), then return the result. ([BR-17](../business-rules.md#br-17-successful-extraction-returns-a-new-succeeded-result), [BR-18](../business-rules.md#br-18-extraction-result-records-processor-and-page-expression))

## Process flow

The following diagram visualizes this operation's control flow.

```mermaid
flowchart TD
	Start(["ExtractPages(documentRange)"]) --> Guard["OP-01: ValidatePdf"]
	Guard --> Open["Open PdfReader,<br>PdfCopy over MemoryStream"]
	Open --> Loop{For each PageRange}
	Loop --> Skip{start and stop<br>both > total?}
	Skip -->|Yes| Loop
	Skip -->|No| Clamp["Clamp start/stop<br>to total pages"]
	Clamp --> Kind{Range kind?}
	Kind -->|Original| CopyAll["AddDocument<br>(entire source)"]
	Kind -->|stop >= start<br>or stop == -1| CopyAsc["Append pages<br>ascending"]
	Kind -->|stop < start| CopyDesc["Append pages<br>descending"]
	CopyAll --> Loop
	CopyAsc --> Loop
	CopyDesc --> Loop
	Loop -->|Done| Wrap["Document.Load(bytes, pdf)"]
	Wrap --> Result(["PdfResult: Succeeded,<br>PdfProcessorType = iTextSharp"])
```

## Failure and edge-case behavior
- Invalid source PDF bytes abort via `PdfException` at the guard step ([BR-03](../business-rules.md#br-03-invalid-pdf-input-aborts-with-pdfexception)) before any pages are copied.
- A page range wholly beyond the document's end contributes no pages to the output and does not raise an error ([BR-12](../business-rules.md#br-12-out-of-range-page-ranges-are-skipped)).
- A descending range (`stop < start`) is not an error condition; it deliberately reverses page order in the output ([BR-16](../business-rules.md#br-16-descending-range-reverses-page-order)).

## Related documentation
- [Operations and processes](../operations-processes.md)
- [Business rules](../business-rules.md)
- [Security and authorization](../security-authorization.md)

## Sources
- `PdfAPI.Core\Services\PdfClientService.cs`
- `PdfAPI.Core\Document.cs`
- `PdfAPI.Core\Extract\DocumentRange.cs`
- `PdfAPI.Core\Extract\PageRange.cs`
- `PdfAPI.Core\PdfResult.cs`
